package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.hash.Hash;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.DataCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryPolicy;
import com.atguigu.tingshu.user.pattern.factory.DeliveryPolicyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;
    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 小程序授权登录
     *
     * @param code 小程序端临时code用于获取微信openId
     * @return map (token: 令牌)
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1 对接微信登录接口,获取openID
            WxMaUserService userService = wxMaService.getUserService();
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
            String openid = sessionInfo.getOpenid();
            //2 查询数据库用户数据
            UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openid));
            //2.1 无->首次登录
            if (userInfo == null) {
                //2.1.1 新增用户记录
                userInfo = new UserInfo();
                userInfo.setWxOpenId(openid);
                userInfo.setNickname("Bugoo");
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-07-21/beb17028-f0b3-41dd-9c77-dd8fac80732c.png");
                userInfoMapper.insert(userInfo);
                //2.1.2 初始化该用户账户记录
                //可用openFeign或MQ均可实现,但都需要考虑数据一致性问题.
                //2.1.2.1 创建账户初始化对象,如果采用vo对象,需要实现序列化接口
                //RabbitMQ生产者端:
                Map<String, Object> mqMap = new HashMap<>();
                mqMap.put("userId", userInfo.getId());
                mqMap.put("amount", BigDecimal.valueOf(100));
                mqMap.put("orderNo", "ZS" +
                        DateUtil.today().replace("-", "") + IdUtil.getSnowflakeNextId());
                mqMap.put("title", "首次注册,赠送内容");
                //2.1.2.2 发送消息至RabbitMQ交换机
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, mqMap);
            }
            //3 生成令牌,返回用户端
            String token = IdUtil.randomUUID();
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
            HashMap<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (WxErrorException e) {
            log.error("登录失败");
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取用户信息
     *
     * @param userId 用户ID
     * @return userInfoVo 用户简单信息
     */
    @Override
    @DataCache(prefix = "user:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        if (userInfo != null) {
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            return userInfoVo;
        }
        return null;
    }

    /**
     * 更新用户信息
     *
     * @param userInfoVo 用户变更信息
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        UserInfo userInfo = BeanUtil.copyProperties(userInfoVo, UserInfo.class);
        userInfo.setId(AuthContextHolder.getUserId());
        userInfoMapper.updateById(userInfo);
    }

    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> trackIdList) {
        //获取专辑或专辑下音频购买情况,返回音频购买信息列表,0=notPaid/1=paid
        //创建音频购买情况列表,默认0=未购买
        HashMap<Long, Integer> trackPaidIdMap = new HashMap<>();
        for (Long trackId : trackIdList) {
            trackPaidIdMap.put(trackId, 0);
        }
        //若购买了整张专辑,专辑下所有音频购买情况修改为1=已购买
        Long selectCount = userPaidAlbumMapper.selectCount(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userId).eq(UserPaidAlbum::getAlbumId, albumId));
        if (selectCount != 0) {
            for (Long trackId : trackIdList) {
                trackPaidIdMap.put(trackId, 1);
            }
        }
        //若未查询到专辑购买信息,再查询音频购买详情
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>()
                .select(UserPaidTrack::getTrackId)
                .eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId));
        //将已购买音频购买详情设置为1=已购买
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
            for (Long trackPaidId : userPaidTrackIdList) {
                trackPaidIdMap.put(trackPaidId, 1);
            }
        }
        return trackPaidIdMap;
    }

    /**
     * 判断用户是否购买过指定专辑
     *
     * @param albumId
     */
    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {
        Long count = userPaidAlbumMapper.selectCount(new LambdaQueryWrapper<UserPaidAlbum>()
                .eq(UserPaidAlbum::getUserId, userId)
                .eq(UserPaidAlbum::getAlbumId, albumId));
        return count > 0;
    }

    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private UserVipServiceMapper userVipServiceMapper;
    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;
    @Autowired
    private DeliveryPolicyFactory deliveryPolicyFactory;

    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = userPaidRecordVo.getItemType();
        
        DeliveryPolicy deliveryPolicy = deliveryPolicyFactory.getDeliveryPolicy(itemType);
        deliveryPolicy.delivery(userPaidRecordVo);
    }

    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }
}